#pragma once
#include"online.hpp"
#include"db.hpp"
#include<unordered_map>
#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
enum room_status
{
    GAME_START,
    GAME_OVER
};
class room
{
    private:
    uint64_t _room_id;                     // 房间号
    std::vector<std::vector<int>> _board; // 棋盘
    uint64_t white_id;               // 白棋用户id
    uint64_t black_id;               // 黑棋用户id
    room_status _status;                   // 房间状态
    online_manager* _online_usr;          // 在线用户管理器
    user_table* _table_usr;              // 用户数据表
    int _player_count;               // 房间内玩家数量
    private:
    bool five(int row,int col,int row_off,int col_off,int color)
    {
        //位置加上偏移量
        int sum=1;
        int row_now=row,col_now=col;
        while(sum<5&&row_now+row_off>=0&&row_now+row_off<BOARD_ROW&&
            col_now+col_off>=0&&col_now+col_off<BOARD_COL&&
            _board[row_now+row_off][col_now+col_off]==color)
        {
            row_now+=row_off;
            col_now+=col_off;
            ++sum;
        }
        row_now=row,col_now=col;
        while(sum<5&&row_now-row_off>=0&&row_now-row_off<BOARD_ROW&&
            col_now-col_off>=0&&col_now-col_off<BOARD_COL&&
            _board[row_now-row_off][col_now-col_off]==color)
        {
            row_now-=row_off;
            col_now-=col_off;
            ++sum;
        }
        return sum>=5;
    }
    uint64_t Check_Win(int row,int col,int color)
    {
        if(five(row,col,0,1,color)||
        five(row,col,1,0,color)||
        five(row,col,1,1,color)||
        five(row,col,1,-1,color))
            return color==CHESS_WHITE?white_id:black_id;
        return 0;
        //返回胜利玩家的id，没有胜利者则返回0
    }
    public:
    room(uint64_t room_id,online_manager* online_usr,user_table* table_usr)
    :_room_id(room_id),_board(BOARD_ROW,std::vector<int>(BOARD_COL,0)),_status(GAME_START),_online_usr(online_usr),_table_usr(table_usr)
    {
        assert(_table_usr);
        DEBUG_LOG("有一个房间%lu创建成功",_room_id);
    }
    ~room()
    {
        DEBUG_LOG("有一个房间%lu销毁成功",_room_id);
    }
    uint64_t Id(){return _room_id;}
    room_status Status(){return _status;}
    int Player_Count(){return _player_count;}
    void Add_white_id(uint64_t id){white_id=id;++_player_count;}
    void Add_balckid(uint64_t id){black_id=id;++_player_count;}
    uint64_t Get_Black_Id(){return black_id;}
    uint64_t Get_White_Id(){return white_id;}
    //下棋
    Json::Value Hanle_Chess(Json::Value& req)
    {
        Json::Value resp=req;
        //判断双方玩家是否都在线，一方掉线则另一方获胜
        uint64_t cur_id=req["uid"].asInt64();
        int row=req["row"].asInt(),col=req["col"].asInt();
        if(_online_usr->is_game_room(white_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="对方掉线，不战而胜";
            resp["winner"]=black_id;
            return resp;
        }
        if(_online_usr->is_game_room(black_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="对方掉线，不战而胜";
            resp["winner"]=white_id;
            return resp;
        }
        //获取走棋位置，判断走棋位置是否合理
        if(_board[row][col]!=0)
        {
            resp["result"]=false;
            resp["reason"]="当前位置已经有了其他棋子";
            return resp;
        }
        int cur_color=cur_id==white_id?CHESS_WHITE:CHESS_BLACK;
        _board[row][col]=cur_color;
        //判断是否有玩家胜利
        uint64_t winner_id=Check_Win(row,col,cur_color);
        if(winner_id!=0)
            resp["reason"]="五子连珠，胜利！";
        resp["result"]=true;
        resp["winner"]=winner_id;
        return resp;
    }
    //聊天
    Json::Value Hanle_Chat(Json::Value& req)
    {
        //检测房间是否一致
        Json::Value resp=req;
        //检测消息中是否包含敏感词
        std::string message=req["message"].asString();
        size_t pos=message.find("垃圾");
        if(pos!=std::string::npos)
        {
            resp["result"]=false;
            resp["reason"]="消息中不能包含敏感词";
            return resp;
        }
        resp["result"]=true;
        return resp;
    }
    //退出
    void Hanle_Exit(uint64_t id)
    {
        //游戏中退出，则对方加一分
        DEBUG_LOG("id:%lu退出",id);
        Json::Value resp;
        if(_status==GAME_START)
        {
            uint64_t winner=id==white_id?black_id:white_id;
            uint64_t loser=id==white_id?white_id:black_id;
            resp["optype"]="put_chess";
            resp["result"]=true;
            resp["reason"]="对方掉线，不战而胜";
            resp["room_id"]=_room_id;
            resp["uid"]=id;
            resp["row"]=-1;
            resp["col"]=-1;
            resp["winner"]=winner;
            _table_usr->win(winner);
            DEBUG_LOG("id:%lu加分",winner);
            _table_usr->lose(loser);
            DEBUG_LOG("id:%lu扣分",loser);
            _status=GAME_OVER;
            broadcast(resp);
        }
        --_player_count;
    }
    //总体处理函数
    void Hanle_Request(Json::Value& req)
    {
        Json::Value resp;
        //判断请求房间号与当前房间号是否匹配
        if(req["room_id"].asUInt64()!=_room_id)
        {
            DEBUG_LOG("目标房间号:%lu,当前房间号:%lu",req["room_id"].asUInt64(),_room_id);
            resp["optype"]=req["optype"].asString();
            resp["result"]=false;
            resp["reason"]="房间号不匹配";
            broadcast(resp);
            return;
        }
        if(req["optype"].asString()=="put_chess"&&_status!=GAME_OVER)
        {
            resp=Hanle_Chess(req);
            if(resp["winner"].asUInt64()!=0)
            {
                uint64_t winner=resp["winner"].asUInt64();
                uint64_t loser=winner==white_id?black_id:white_id;
                _table_usr->win(winner);
                _table_usr->lose(loser);
                _status=GAME_OVER;
            }
        } 
        else if(req["optype"].asString()=="chat")
            resp=Hanle_Chat(req);//判断是否因为敏感词对一方发送
        else
        {
            DEBUG_LOG("未知类型:%s",req["optype"].asString().c_str());
            resp["optype"]=req["optype"].asString();
            resp["result"]=false;
            resp["reason"]="未知请求类型";
        }
        broadcast(resp);
    }
    //广播
    void broadcast(Json::Value& rsp)
    {
        std::string body;
        json_util::Serialize(rsp,body);
        //发送响应信息
        websocket_server::connection_ptr wconn=_online_usr->get_conn_from_room(white_id);
        if(wconn!=nullptr)
            wconn->send(body);
        websocket_server::connection_ptr bconn=_online_usr->get_conn_from_room(black_id);
        if(bconn!=nullptr)
            bconn->send(body);
        return;
    }
};

using room_ptr=std::shared_ptr<room>;
class room_manager
{
    private:
    std::mutex _mutex;
    std::unordered_map<uint64_t,room_ptr> _rooms;//通过房间id找房间管理对象
    std::unordered_map<uint64_t,uint64_t> _users;//通过用户id找房间id
    online_manager* _online_usr;          // 在线用户管理器
    user_table* _table_usr;              // 用户数据表
    uint64_t _next_rid;//下一个房间id
    public:
    room_manager(online_manager* online_usr,user_table* table_usr):
    _online_usr(online_usr),_table_usr(table_usr),_next_rid(0)
    {
        DEBUG_LOG("游戏房间管理模块初始化成功!");
        assert(_table_usr);
    }
    ~room_manager(){}
    //通过用户id寻找房间
    room_ptr get_room_by_user_id(uint64_t uid)
    {
        std::unique_lock<std::mutex> _guard(_mutex);
        auto it=_users.find(uid);
        if(it==_users.end())
            return room_ptr();
        uint64_t rid=it->second;
        auto rit=_rooms.find(rid);
        if(rit==_rooms.end())
            return room_ptr();
        return rit->second;
    }
    //通过房间id寻找房间
    room_ptr get_room_by_id(uint64_t rid)
    {
        std::unique_lock<std::mutex> _guard(_mutex);
        auto it=_rooms.find(rid);
        if(it==_rooms.end())
            return room_ptr();
        return it->second;
    }
    //为两个用户创建房间
    room_ptr create_room(uint64_t uid1,uint64_t uid2)
    {
        //只有两个用户都在游戏大厅，才创建房间，否则返回一个空对象
        if(_online_usr->is_game_hall(uid1)==false||_online_usr->is_game_hall(uid2)==false)
            return room_ptr();
        assert(_table_usr);
        room_ptr new_roon=std::make_shared<room>(++_next_rid,_online_usr,_table_usr);
        DEBUG_LOG("新房间id:%lu",_next_rid);
        new_roon->Add_balckid(uid1);
        new_roon->Add_white_id(uid2);
        std::unique_lock<std::mutex> guard(_mutex);
        _rooms.insert(std::make_pair(_next_rid,new_roon));
        _users.insert(std::make_pair(uid1,_next_rid));
        _users.insert(std::make_pair(uid2,_next_rid));
        return new_roon;
    }
    //通过房间id销毁房间
    void remove_room(uint64_t rid)
    {
        room_ptr rm_room=get_room_by_id(rid);
        if(rm_room.get()==nullptr)
            return;
        uint64_t uid1=rm_room->Get_Black_Id();
        uint64_t uid2=rm_room->Get_White_Id();
        std::unique_lock<std::mutex> _guard(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        _rooms.erase(rid);
    }
    //删除房间中指定用户，如果没有用户了，则清除房间
    void remove_room_user(uint64_t uid)
    {
        room_ptr rm_room=get_room_by_user_id(uid);
        if(rm_room.get()==nullptr)
            return;
        //处理玩家退出事件
        rm_room->Hanle_Exit(uid);
        //一旦房间中玩家为空，清除房间
        if(rm_room->Player_Count()==0)
            remove_room(rm_room->Id());
    }
};